////////////////////////////////////////////////////////////////////////
// This file is part of the simpleSID framework 
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
//
// Copyright (c)Richard White, 2009
// See License.txt for a disclaimer of all warranties
// and licensing information

#include <stdafx.h>
#include "RWSID.h"

//const float RWSID::PI = 3.1415926535897932384626433832795f;

RWSID::RWSID(float fSampleFreq)
: m_fSampleFreq(fSampleFreq), m_nReadPos(0), m_nWritePos(0), m_fFracPos(0)
{
	//Note I am being bad by just setting the thing at pal its cause i'm lazy this should be very much changed
	m_fCyclesPerSample = 985248.f / fSampleFreq;
	m_fSamplesPerCycle = fSampleFreq / 985248.f;

	//memset(&m_fRingBuffer, 0, sizeof(m_fRingBuffer));
	
	//We need to setup what voice sync to what as first voice is syncd with 3 2 with the fist and 3rd with the second
	m_Voice[0].SetSyncSource(&m_Voice[2]);
	m_Voice[1].SetSyncSource(&m_Voice[0]);
	m_Voice[2].SetSyncSource(&m_Voice[1]);
}

RWSID::~RWSID()
{

}

void RWSID::InitSID()
{

}

void RWSID::ResetSID()
{
	//We need to reset all the voices here and reset the filter here NB envelope is reset with voice
	for (unsigned int i = 0; i < 3; i++)
		m_Voice[i].Reset();



}

void RWSID::WriteSIDReg(unsigned char byOffset, unsigned char byValue)
{
	//29 register from 0-28
	int nVoice;
	if ((byOffset >= 0) && (byOffset <= 6)) 
		nVoice = 0;
	if ((byOffset >= 7) && (byOffset <= 13)) 
	{
		nVoice = 1;
		byOffset -= 7;
	}
	if ((byOffset >= 14) && (byOffset <= 20))
	{
		nVoice = 2;
		byOffset -= 14;
	}

	switch (byOffset)
	{
	case 0x0:
		m_Voice[nVoice].SetFreqLO(byValue);
		break;
	case 0x01:
		m_Voice[nVoice].SetFreqHI(byValue);
		break;
	case 0x02:
		m_Voice[nVoice].SetPWLO(byValue);
		break;
	case 0x03:
		m_Voice[nVoice].SetPWHI(byValue);
		break;
	case 0x04:
		m_Voice[nVoice].SetControlReg(byValue);
		break;
	case 0x05:
		m_Voice[nVoice].SetAttackDecay(byValue);
		break;
	case 0x06:
		m_Voice[nVoice].SetSustainRelease(byValue);
		break;
	case 0x15:
		m_Filter.SetFCLo(byValue);
		break;
	case 0x16:
		m_Filter.SetFCHi(byValue);
		break;
	case 0x17:
		m_Filter.SetResFilt(byValue);
		break;
	case 0x18:
		m_Filter.SetModeVol(byValue);
		break;
	}

}

unsigned char RWSID::ReadSIDReg(unsigned char byOffset)
{
	//29 register from 0-28 will return bus_value if not a readable value pot control is not implemented

	switch (byOffset)
	{
	case 27:
		m_Voice[2].ReadOsc(); // Read third voice as a register
		break;
	case 28:
		//m_Voice[2]
		break;
	}
	return 0; //temp just to compile
}

void RWSID::SetWaveForVoice(int nVoice, WaveType wt, bool bSet)
{
	if (nVoice < 4)
	{
		switch (wt)
		{
		case kSaw:
			m_Voice[nVoice - 1].SetSawWave(bSet);
			break;
		case kPulse:
			m_Voice[nVoice - 1].SetPulseWave(bSet);
			break;
		case kTri:
			m_Voice[nVoice - 1].SetTriWave(bSet);
			break;
		case kNoise:
			m_Voice[nVoice - 1].SetNoiseWave(bSet);
			break;
		}
	}
}

void RWSID::SetFreqForVoice(int nVoice, unsigned char Lo, unsigned char Hi)
{
	if (nVoice < 4)
	{
		m_Voice[nVoice - 1].SetFreqLO(Lo);
		m_Voice[nVoice - 1].SetFreqHI(Hi);
	}
}

void RWSID::SetPulseWidthForVoice(int nVoice, unsigned char Lo, unsigned char Hi)
{
	if (nVoice < 4)
	{
		m_Voice[nVoice - 1].SetPWLO(Lo);
		m_Voice[nVoice - 1].SetPWHI(Hi);
	}
}

void RWSID::SetTestForVoice(int nVoice, bool bTest)
{
	if (nVoice < 4)
		m_Voice[nVoice - 1].SetTestBit(bTest);
}

void RWSID::SetSyncForVoice(int nVoice, bool bSync)
{
	if (nVoice < 4)
		m_Voice[nVoice - 1].SetSyncBit(bSync);
}

void RWSID::SetRingModForVoice(int nVoice, bool bRingMod)
{
	if (nVoice < 4)
		m_Voice[nVoice - 1].SetRingModBit(bRingMod);
}

void RWSID::SetAttackForVoice(int nVoice, unsigned char byAttack)
{
	if (nVoice < 4)
		m_Voice[nVoice - 1].SetAttack(byAttack);
}

void RWSID::SetDecayForVoice(int nVoice, unsigned char byDecay)
{
	if (nVoice < 4)
		m_Voice[nVoice - 1].SetDecay(byDecay);
}

void RWSID::SetSustainForVoice(int nVoice, unsigned char bySustain)
{
	if (nVoice < 4)
		m_Voice[nVoice - 1].SetSustain(bySustain);
}

void RWSID::SetReleaseForVoice(int nVoice, unsigned char byRelease)
{
	if (nVoice < 4)
		m_Voice[nVoice - 1].SetRelease(byRelease);
}

void RWSID::SetGateForVoice(int nVoice, bool bGate)
{
	if (nVoice < 4)
		m_Voice[nVoice - 1].SetGate(bGate);
}

void RWSID::SetVol(unsigned char byVol)
{
	m_Filter.SetVol(byVol);
}

void RWSID::SetMuteForVoice(int nVoice, bool bState)
{
	if (nVoice < 4)
	{
		m_Voice[nVoice - 1].SetMuteState(bState);
	}
}

void RWSID::SetFilterFreq(float fFreq)
{
	m_Filter.SetFreq(fFreq);
}

void RWSID::SetFilterFCLo(unsigned char byFCLo)
{
	m_Filter.SetFCLo(byFCLo);
}

void RWSID::SetFilterFCHi(unsigned char byFCHi)
{
	m_Filter.SetFCHi(byFCHi);
}

void RWSID::SetFilterRes(unsigned char byRes)
{
	m_Filter.SetRes(byRes);
}

void RWSID::SetFilterMode(FiltMode mode, bool bState)
{
	switch (mode)
	{
	case kLP:
		m_Filter.SetModeLP(bState);
		break;
	case kBP:
		m_Filter.SetModeBP(bState);
		break;
	case kHP:
		m_Filter.SetModeHP(bState);
		break;
	case k3Off:
		//ToDo;
		break;
	}
}

void RWSID::SetFilterRouting(int nVoice, bool bState)
{
	if (nVoice < 4)
	{
		switch (nVoice)
		{
		case 1:
			m_Filter.SetFiltVoice1(bState);
			break;
		case 2:
			m_Filter.SetFiltVoice2(bState);
			break;
		case 3:
			m_Filter.SetFiltVoice3(bState);
			break;
		}
	}
}

void RWSID::EnableFilter(bool bState)
{
	m_Filter.EnableFiler(bState);
}

void RWSID::ResetVoice(int nVoice)
{
	if (nVoice < 4)
		m_Voice[nVoice - 1].Reset();
}

void RWSID::Process()
{
	int i;
	//We first clock our voices this incudes the envelope
	for (i = 0; i < 3; i++)
	{
		m_Voice[i].Process();
	}
	
	//Then we need to synchronize
	for (i = 0; i < 3; i++)
	{
		m_Voice[i].Synchronize();
	}
	
}

void RWSID::Process(int iCycles) //TODO: Need to change to ints
{
	if (iCycles <= 0)
	{
		return; //No go it no cycles
	}

	for (int i = 0; i < 3; i++) // For each voice
	{	
		m_Voice[i].Process(iCycles);
	}
}

float RWSID::FloatOutput()
{
	int iVoice1 = m_Voice[0].Output();
	int iVoice2 = m_Voice[1].Output();
	int iVoice3 = m_Voice[2].Output();

	float fOut = m_Filter.Process(iVoice1, iVoice2, iVoice3); //Is a 20 bit float me says

	//We have 0 - 255 Envelope we also have a 12 bit oscillator which has been offset so +1 -1 and 3 voices so need to scale down for float out
	fOut =  fOut / (256.f * 4096.f * 3.f);
	return fOut;
}

int RWSID::Render(float *fBuffer, unsigned long ulLen, int iCycles)
{
	float fNextSampleCycles = 0.f;
	int nSampleCycles = 0;
	float fPreviousSample = 0.f;
	unsigned int val = 0;
	int i = 0;
	
	while(iCycles > 0)
	{
		fNextSampleCycles = m_fFracPos + m_fCyclesPerSample;
		int nSampleCycles = static_cast<int>(fNextSampleCycles);
		if (iCycles < nSampleCycles)
			break;

		if (val >= ulLen)
			return val;
	
		Process(nSampleCycles - 1);
		float PrevSample = FloatOutput();
		Process();

		iCycles -= nSampleCycles;
		m_fFracPos = fNextSampleCycles - nSampleCycles;
		
		float CurSample = FloatOutput();

		float fOut = Interpolate(PrevSample, CurSample, m_fFracPos);
		fBuffer[val++] = fOut;

	}
	return 0;
}

//void RWSID::MakeWindowedSinc()
//{
	//float xScale = PI * m_fCyclesPerSample;
	//float *pFIRTable = m_fFIRTable + FIRSIZE;

	//for (int i = -FIRSIZE; i <= FIRSIZE; i++)
		//pFIRTable[i] = m_fCyclesPerSample * MakeSinc(i * xScale) * MakeHamming(i / FIRSIZE);
//}

//float RWSID::MakeSinc(float x)
//{
	//return x ? sinf(x) / x : 1; 
//}

//float RWSID::MakeHamming(float x)
//{
	//return (x > -1 && x < 1) ? sqrtf(cosf(x * PI / 2)) : 0;
//}

float RWSID::Interpolate(float a, float b, float factor)
{
	return a + factor * (b - a);
}